package cn.iocoder.yudao.module.hs.service.materialbox;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.hs.controller.admin.materialbox.vo.MaterialBoxPageReqVO;
import cn.iocoder.yudao.module.hs.controller.admin.materialbox.vo.MaterialBoxSaveReqVO;
import cn.iocoder.yudao.module.hs.dal.dataobject.material.MaterialDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.materialbox.MaterialBoxDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.rfid.RfidDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.tray.TrayDO;
import cn.iocoder.yudao.module.hs.dal.mysql.material.MaterialMapper;
import cn.iocoder.yudao.module.hs.dal.mysql.materialbox.MaterialBoxMapper;
import cn.iocoder.yudao.module.hs.enums.warehouse.WarehouseStatusEnum;
import cn.iocoder.yudao.module.hs.service.entryorderdetail.EntryOrderDetailService;
import cn.iocoder.yudao.module.hs.service.material.MaterialService;
import cn.iocoder.yudao.module.hs.service.rfid.RfidService;
import cn.iocoder.yudao.module.hs.service.tray.TrayService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.hs.enums.ErrorCodeConstants.MATERIAL_BOX_NOT_EXISTS;
import static cn.iocoder.yudao.module.hs.enums.ErrorCodeConstants.RFID_NOT_EXISTS;

/**
 * 物料箱信息 Service 实现类
 *
 * @author 航盛
 */
@Service
@Validated
public class MaterialBoxServiceImpl implements MaterialBoxService {

    @Resource
    private MaterialBoxMapper materialBoxMapper;
    @Resource
    private RfidService rfidService;
    @Resource
    private MaterialService materialService;
    @Resource
    private MaterialMapper materialMapper;
    @Resource
    private TrayService trayService;
    @Lazy
    @Resource
    private EntryOrderDetailService entryOrderDetailService;

    @Override
    public Long createMaterialBox(MaterialBoxSaveReqVO createReqVO) {
        // 插入
        MaterialBoxDO materialBox = BeanUtils.toBean(createReqVO, MaterialBoxDO.class);
        materialBoxMapper.insert(materialBox);
        // 返回
        return materialBox.getId();
    }

    @Override
    public void createMaterialBoxes(List<MaterialBoxSaveReqVO> materialBoxList) {
        List<MaterialBoxDO> materialBoxDOList = BeanUtils.toBean(materialBoxList, MaterialBoxDO.class);
        materialBoxMapper.insertBatch(materialBoxDOList);
    }

    @Override
    public void updateMaterialBox(MaterialBoxSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialBoxExists(updateReqVO.getId());
        // 更新
        MaterialBoxDO updateObj = BeanUtils.toBean(updateReqVO, MaterialBoxDO.class);
        materialBoxMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterialBox(Long id) {
        // 校验存在
        validateMaterialBoxExists(id);
        // 删除
        materialBoxMapper.deleteById(id);
    }

    private void validateMaterialBoxExists(Long id) {
        if (materialBoxMapper.selectById(id) == null) {
            throw exception(MATERIAL_BOX_NOT_EXISTS);
        }
    }

    private MaterialBoxDO validateMaterialBoxExists(String code) {
        MaterialBoxDO materialBox = getMaterialBox(code);
        if (ObjectUtil.isNull(materialBox)) {
            throw exception(MATERIAL_BOX_NOT_EXISTS);
        }
        return materialBox;
    }

    @Override
    public MaterialBoxDO getMaterialBox(Long id) {
        return materialBoxMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialBoxDO> getMaterialBoxPage(MaterialBoxPageReqVO pageReqVO) {
        return materialBoxMapper.selectPage(pageReqVO);
    }

    @Override
    public void updateMaterialBoxEntryByTrayCode(String trayCode, Long warehouseId, Long deptId) {
        // 查询对应的托盘编码是否有物料箱
        List<MaterialBoxDO> list = materialBoxMapper.selectListByTrayCode(trayCode);
        if (CollectionUtil.isNotEmpty(list)) {
            list.forEach(materialBoxDO -> {
                materialBoxDO.setStatus(WarehouseStatusEnum.ENTRY.getStatus());
                materialBoxDO.setWarehousingTime(LocalDateTime.now());
                materialBoxDO.setWarehouseId(warehouseId);
                materialBoxDO.setDeptId(deptId);
            });
            materialBoxMapper.updateBatch(list);
        }
    }

    @Override
    public void updateMaterialBoxTray(List<MaterialBoxDO> materialBoxDOList, String trayCode) {
        if (CollectionUtil.isNotEmpty(materialBoxDOList)) {
            materialBoxDOList.forEach(materialBoxDO -> {
                materialBoxDO.setTrayCode(trayCode);
            });
            materialBoxMapper.updateBatch(materialBoxDOList);
            List<String> boxCodeList = CollectionUtils.convertList(materialBoxDOList, MaterialBoxDO::getCode);
            List<MaterialDO> materialListByBoxCode = materialService.getMaterialListByBoxCode(boxCodeList);
            materialService.updateMaterialTray(materialListByBoxCode, trayCode);
        }
    }

    @Override
    public MaterialBoxDO getMaterialBox(String boxCode) {
        return materialBoxMapper.selectOne(MaterialBoxDO::getCode, boxCode);
    }

    @Override
    public void changeMaterialBox(String rfidCode, String boxCode) {
        MaterialBoxDO materialBox = getMaterialBox(boxCode);
        if (materialBox == null) {
            throw exception(MATERIAL_BOX_NOT_EXISTS);
        }
        RfidDO rfid = rfidService.getRfid(rfidCode);
        if (rfid == null) {
            throw exception(RFID_NOT_EXISTS);
        }
        materialBox.setTrayCode(rfid.getTrayCode());
        materialBoxMapper.updateById(materialBox);
    }

    @Override
    public void updateTrayCodeByCode(String trayCode, String boxCode) {
        MaterialBoxDO materialBox = validateMaterialBoxExists(boxCode);
        materialBox.setTrayCode(trayCode);
        TrayDO tray = trayService.getTray(trayCode);
        if (tray != null && WarehouseStatusEnum.ENTRY.getStatus().equals(tray.getStatus())) {
            List<MaterialDO> materialListByBoxCode = materialService.getMaterialListByBoxCode(boxCode);
            if (CollUtil.isNotEmpty(materialListByBoxCode)) {
                for (MaterialDO material : materialListByBoxCode) {
                    LambdaUpdateWrapper<MaterialDO> updateWrapper = new LambdaUpdateWrapper<MaterialDO>();
                    updateWrapper.set(MaterialDO::getStatus, WarehouseStatusEnum.ENTRY.getStatus())
                            .set(MaterialDO::getWarehouseId, tray.getWarehouseId())
                            .set(MaterialDO::getShipTime, null)
                            .set(ObjectUtil.isNull(material.getTripartiteFifoTime()), MaterialDO::getTripartiteFifoTime, material.getFifoTime())
                            .eq(MaterialDO::getId, material.getId());
                    materialMapper.update(updateWrapper);
                    entryOrderDetailService.insertEnterOrderDetail(tray, material);
                }
            }
            materialBox.setStatus(WarehouseStatusEnum.ENTRY.getStatus());
        }
        materialBoxMapper.updateById(materialBox);
    }

    @Override
    public void updateNullTrayCodeByCode(String boxCode) {
        MaterialBoxDO materialBox = validateMaterialBoxExists(boxCode);
        materialBoxMapper.update(new LambdaUpdateWrapper<MaterialBoxDO>()
                .set(MaterialBoxDO::getTrayCode, null)
                .eq(MaterialBoxDO::getId, materialBox.getId()));
    }

    @Override
    public void updateMaterialBoxOut(Set<String> boxCodes, Long toWarehouseDeptId) {
        LambdaUpdateWrapper<MaterialBoxDO> updateWrapper = new LambdaUpdateWrapper<MaterialBoxDO>()
                .set(MaterialBoxDO::getStatus, WarehouseStatusEnum.SHIP.getStatus())
                .set(MaterialBoxDO::getWarehouseId, null)
                .set(toWarehouseDeptId != null, MaterialBoxDO::getDeptId, toWarehouseDeptId)
                .in(MaterialBoxDO::getCode, boxCodes);
        materialBoxMapper.update(updateWrapper);
    }

    @Override
    public void updateMaterialBoxRollback(Set<String> boxCodes, Long warehouseId) {
        LambdaUpdateWrapper<MaterialBoxDO> updateWrapper = new LambdaUpdateWrapper<MaterialBoxDO>()
                .set(MaterialBoxDO::getStatus, WarehouseStatusEnum.ENTRY.getStatus())
                .set(MaterialBoxDO::getWarehouseId, warehouseId)
                .in(MaterialBoxDO::getCode, boxCodes);
        materialBoxMapper.update(updateWrapper);
    }

    @Override
    public Boolean checkBoxInTray(String boxCode, String trayCode) {
        MaterialBoxDO materialBox = getMaterialBox(boxCode);
        if (materialBox == null) {
            throw exception(MATERIAL_BOX_NOT_EXISTS);
        }
        if (materialBox.getTrayCode().equals(trayCode)) {
            return Boolean.TRUE;
        } else {
            RfidDO rfid = rfidService.getRfid(trayCode);
            if (rfid == null) {
                throw exception(RFID_NOT_EXISTS);
            }
            if (materialBox.getTrayCode().equals(rfid.getTrayCode())) {
                return Boolean.TRUE;
            } else {
                return Boolean.FALSE;
            }
        }
    }
}
